package com.kob.backend.consumer.utils;

import com.alibaba.fastjson2.JSONObject;
import com.kob.backend.consumer.WebSocketServer;
import com.kob.backend.mapper.UserMapper;
import com.kob.backend.pojo.Bot;
import com.kob.backend.pojo.Record;
import com.kob.backend.pojo.User;
import org.springframework.security.core.parameters.P;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Random;
import java.util.concurrent.locks.ReentrantLock;

public class Game extends Thread{
    private final Integer rows;
    private final Integer cols;
    private final Integer inner_walls_count;
    private final int[][] g;
    private final static int[] dx = {-1, 0, 1, 0}, dy = {0, 1, 0, -1};
    //两名玩家
    private final Player playerA,playerB;
    //存两名玩家下一步操作
    private Integer nextStepA=null;
    private Integer nextStepB=null;
    //加锁
    private ReentrantLock lock=new ReentrantLock();
    //整个游戏的状态
    private String status="playing";//playing->finished
    //存哪个蛇输了
    private String loser="";//all:平局，A:A输，B:B输
    private final static String addBotUrl="http://127.0.0.1:3002/bot/add/";

    public Game(Integer rows,
                Integer cols,
                Integer inner_walls_count,
                Integer idA,
                Bot botA,
                Integer idB,
                Bot botB
    ) {
        this.rows = rows;
        this.cols = cols;
        this.inner_walls_count = inner_walls_count;
        this.g = new int[rows][cols];

        Integer botIdA=-1,botIdB=-1;
        String botCodeA="",botCodeB="";
        if (botA!=null){
            botIdA=botA.getId();
            botCodeA=botA.getContent();
        }
        if (botB!=null){
            botIdB=botB.getId();
            botCodeB=botB.getContent();
        }

        //实例化两名玩家
        playerA=new Player(idA,botIdA,botCodeA,rows-2,1,new ArrayList<>());
        playerB=new Player(idB,botIdB,botCodeB,1,cols-2,new ArrayList<>());
    }

    public Player getPlayerA(){
        return playerA;
    }

    public Player getPlayerB(){
        return playerB;
    }

    public void setNextStepA(Integer nextStepA){
        lock.lock();//上锁
        try {
            this.nextStepA=nextStepA;//执行本函数
        }finally {//不finally:管有没有报异常都要执行
            lock.unlock();//解锁
        }
    }

    public void setNextStepB(Integer nextStepB){
        lock.lock();//上锁
        try {
            this.nextStepB=nextStepB;//执行本函数
        }finally {
            lock.unlock();//解锁
        }
    }

    public int[][] getG() {
        return g;
    }

    private boolean check_connectivity(int sx, int sy, int tx, int ty) {
        if (sx == tx && sy == ty) return true;
        g[sx][sy] = 1;

        for (int i = 0; i < 4; i ++ ) {
            int x = sx + dx[i], y = sy + dy[i];
            if (x >= 0 && x < this.rows && y >= 0 && y < this.cols && g[x][y] == 0) {
                if (check_connectivity(x, y, tx, ty)) {
                    g[sx][sy] = 0;
                    return true;
                }
            }
        }

        g[sx][sy] = 0;
        return false;
    }

    private boolean draw() {  // 画地图
        for (int i = 0; i < this.rows; i ++ ) {
            for (int j = 0; j < this.cols; j ++ ) {
                g[i][j] = 0;
            }
        }

        for (int r = 0; r < this.rows; r ++ ) {
            g[r][0] = g[r][this.cols - 1] = 1;
        }
        for (int c = 0; c < this.cols; c ++ ) {
            g[0][c] = g[this.rows - 1][c] = 1;
        }

        Random random = new Random();
        for (int i = 0; i < this.inner_walls_count / 2; i ++ ) {
            for (int j = 0; j < 1000; j ++ ) {
                int r = random.nextInt(this.rows);
                int c = random.nextInt(this.cols);

                if (g[r][c] == 1 || g[this.rows - 1 - r][this.cols - 1 - c] == 1)
                    continue;
                if (r == this.rows - 2 && c == 1 || r == 1 && c == this.cols - 2)
                    continue;

                g[r][c] = g[this.rows - 1 - r][this.cols - 1 - c] = 1;
                break;
            }
        }

        return check_connectivity(this.rows - 2, 1, 1, this.cols - 2);
    }

    public void createMap() {
        for (int i = 0; i < 1000; i ++ ) {
            if (draw())
                break;
        }
    }

    //将当前的局部信息，编码成字符串
    private String getInput(Player player){
        Player me,you;
        if (playerA.getId().equals(player.getId())){
            me=playerA;
            you=playerB;
        }else {
            me=playerB;
            you=playerA;
        }

        return getMapString()+"#"+
                me.getSx()+"#"+
                me.getSy()+"#("+
                me.getStepsString()+")#"+
                you.getSx()+"#"+
                you.getSy()+"#("+
                you.getStepsString()+")";
    }

    private void sendBotCode(Player player){
        if (player.getBotId().equals(-1))return;//亲自出马,不用执行函数体
        MultiValueMap<String,String> data=new LinkedMultiValueMap<>();
        data.add("user_id",player.getId().toString());
        data.add("bot_code",player.getBotCode());
        data.add("input",getInput(player));

        WebSocketServer.restTemplate.postForObject(addBotUrl,data,String.class);
    }

    //辅助函数实现下一步操作
    private boolean nextStep(){//等待两名玩家下一步操作
        try {
            Thread.sleep(200);//防止玩家输入过快而丢失中间指令,web200ms才能画一格，200ms内接受多个指令的话，只留最后一个
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        sendBotCode(playerA);
        sendBotCode(playerB);

        for(int i=0;i<50;i++){
            try {
                Thread.sleep(100);//停止1000ms,每次操作前先sleep1s
                lock.lock();
                try {
                    //判断俩玩家是否都进行输入了
                    if(nextStepA!=null&&nextStepB!=null){
                        //把俩玩家指令存下来
                        playerA.getSteps().add(nextStepA);
                        playerB.getSteps().add(nextStepB);
                        return true;
                    }
                }finally {
                    lock.unlock();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        return false;
    }

    private boolean check_valid(List<Cell> cellsA, List<Cell> cellsB) {//判断某一个cell是否合法
        int n = cellsA.size();
        Cell cell = cellsA.get(n - 1);
        if (g[cell.x][cell.y] == 1) //是墙
            return false;

        for (int i = 0; i < n - 1; i ++ ) {//和A重合
            if (cellsA.get(i).x == cell.x && cellsA.get(i).y == cell.y)
                return false;
        }

        for (int i = 0; i < n - 1; i ++ ) {
            if (cellsB.get(i).x == cell.x && cellsB.get(i).y == cell.y)
                return false;
        }

        return true;
    }


    private void judge(){//判断两名玩家下一步操作是否合法
        //先取出俩蛇
        List<Cell> cellsA = playerA.getCells();
        List<Cell> cellsB = playerB.getCells();

        boolean validA = check_valid(cellsA, cellsB);
        boolean validB = check_valid(cellsB, cellsA);
        if (!validA || !validB) {
            status = "finished";

            if (!validA && !validB) {
                loser = "all";
            } else if (!validA) {
                loser = "A";
            } else {
                loser = "B";
            }
        }

    }

    private void sendAllMessage(String message){//向每一个人广播信息
        if (WebSocketServer.users.get(playerA.getId())!=null){
            //先获取玩家的连接,再经由连接发送信息
            WebSocketServer.users.get(playerA.getId()).sendMessage(message);
        }
        if (WebSocketServer.users.get(playerB.getId())!=null)
            WebSocketServer.users.get(playerB.getId()).sendMessage(message);
    }

    private void sendMove(){//向两个client传递移动信息
        //读入nextStepAB:加锁
        lock.lock();
        try {
            JSONObject resp=new JSONObject();
            resp.put("event","move");
            resp.put("a_direction",nextStepA);
            resp.put("b_direction",nextStepB);
            sendAllMessage(resp.toJSONString());//返回信息
            //把缓冲区内移动指令读完以后，需要清空以准备下次读入指令
            nextStepA=nextStepB=null;
        }finally {
            lock.unlock();
        }
    }

    private String getMapString() {
        StringBuilder res = new StringBuilder();
        for (int i = 0; i < rows; i ++ ) {
            for (int j = 0; j < cols; j ++ ) {
                res.append(g[i][j]);
            }
        }
        return res.toString();
    }

    //更新玩家积分
    private void upDateUserRating(Player player,Integer rating){
        User user=WebSocketServer.userMapper.selectById(player.getId());
        user.setRating(rating);
        WebSocketServer.userMapper.updateById(user);
    }

    //传送对局信息到数据库
    private void saveToDatabase() {
        Integer ratingA=WebSocketServer.userMapper.selectById(playerA.getId()).getRating();
        Integer ratingB=WebSocketServer.userMapper.selectById(playerB.getId()).getRating();

        if ("A".equals(loser)){
            ratingA-=2;
            ratingB+=5;
        } else if ("B".equals(loser)) {
            ratingA+=5;
            ratingB-=2;
        }

        upDateUserRating(playerA,ratingA);
        upDateUserRating(playerB,ratingB);

        Record record = new Record(
                null,
                playerA.getId(),
                playerA.getSx(),
                playerA.getSy(),
                playerB.getId(),
                playerB.getSx(),
                playerB.getSy(),
                playerA.getStepsString(),
                playerB.getStepsString(),
                getMapString(),
                loser,
                new Date()
        );

        WebSocketServer.recordMapper.insert(record);
    }

    private void sendResult(){//向两个client公布结果
        JSONObject resp=new JSONObject();
        resp.put("event","result");//event表示当前传播的是什么信息
        resp.put("loser",loser);
        saveToDatabase();
        sendAllMessage(resp.toJSONString());
    }

    //thread类的入口函数,也是新线程入口函数
    @Override
    public void run() {
        //另起一个线程，这个线程需要做：等待指令输入
        for(int i=0;i<1000;i++){//一把1000步之内一定可以结束,设置循环目的：避免可能的死循环
            if(nextStep()){//获取到了下一步指令
                judge();
                if (status.equals("playing")){//输入合法且游戏正在进行
                    sendMove();
                }else {//游戏已经结束
                    sendResult();//向前端返回结果
                    break;
                }
            }else {
                status="finished";
                lock.lock();
                try {
                    if(nextStepA==null&&nextStepB==null){//都不输入了（超时）表示平局
                        loser="all";
                    }else if(nextStepA==null){
                        loser="A";
                    }else {
                        loser="B";
                    }
                }finally {
                    lock.unlock();
                }

                //结束之前向俩玩家发送消息
                sendResult();
                break;//游戏结束
            }
        }
    }
}
